mod config;
mod controllers;
mod db;
mod domain;
mod entities;
mod models;
mod services;

#[path = "Lib/Utils/AudioDataConverter.rs"]
pub(crate) mod audio_data_converter;
#[path = "Impls/StationAudioView/SingleStationDataMatrix.rs"]
pub mod single_station_data_matrix;
#[path = "Impls/StationAudioView/FileReaders/SoundWaveReaderService.rs"]
pub mod sound_wave_reader_service;
#[path = "Lib/Models/Realtime/StationDataMatrix.rs"]
pub mod station_data_matrix;

use axum::{Router, middleware as axum_middleware, routing::get};
use axum_ifnk_common::prelude::*;
use color_eyre::eyre::Result as EyreResult;
use services::JsonDataService;
use sound_wave_reader_service::SoundWaveReaderService;
use std::path::Path;
use std::sync::Arc;
use tower_http::trace::TraceLayer;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
use utoipauto::utoipauto;

use controllers::root;

/// 应用状态
#[derive(Clone)]
pub struct AppState {
    pub db: sea_orm::DatabaseConnection,
    pub sound_wave_reader: Arc<SoundWaveReaderService>,
    pub json_data_service: Arc<JsonDataService>,
}

/// API 文档定义
#[utoipauto(paths = "./axum-ifnk-study/src/controllers")]
#[derive(OpenApi)]
#[openapi(
    tags(
        (name = "健康检查", description = "服务健康检查相关接口"),
        (name = "用户管理", description = "用户管理相关接口"),
        (name = "错误处理测试", description = "各种错误处理场景测试"),
        (name = "声波历史", description = "历史项目与声波文件读取接口")
    ),
    info(
        title = "Axum API 文档",
        version = "1.0.0",
        description = "基于 Axum 框架的 RESTful API",
        contact(
            name = "API Support",
            email = "support@example.com"
        )
    )
)]
struct ApiDoc;

#[tokio::main]
async fn main() -> EyreResult<()> {
    color_eyre::install()?;

    // 加载配置
    let config = config::load_config()?;

    // 初始化日志
    let _guard = setup_tracing(&config.logging)
        .map_err(|e| color_eyre::eyre::eyre!("{}", e))?;

    // 设置 panic hook
    setup_panic_hook();

    // 启用 backtrace
    unsafe {
        std::env::set_var("RUST_BACKTRACE", "1");
    }

    tracing::info!("配置加载成功");
    tracing::info!("服务器配置: {}:{}", config.server.host, config.server.port);

    // 初始化数据库连接
    let data_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("data.db");
    let db_url = format!("sqlite://{}?mode=rwc", data_path.to_string_lossy());
    let db = db::init_db(&db_url).await?;

    tracing::info!("数据库初始化成功");

    // 创建应用状态
    let sound_wave_reader = Arc::new(SoundWaveReaderService::new());
    let data_root = Path::new(&config.paths.data_root);
    let json_data_service = Arc::new(
        JsonDataService::new(data_root)
            .map_err(|e| color_eyre::eyre::eyre!("{}", e))?
    );
    let state = AppState {
        db,
        sound_wave_reader,
        json_data_service,
    };

    // 构建应用路由
    let app = Router::new()
        .route("/", get(root::root))
        .merge(controllers::create_routes())
        // 集成 Swagger UI
        .merge(SwaggerUi::new("/swagger").url("/api-docs/openapi.json", ApiDoc::openapi()))
        // 添加 HTTP 请求追踪中间件
        .layer(TraceLayer::new_for_http())
        // 添加 panic 捕获中间件(必须在最外层)
        .layer(axum_middleware::from_fn(catch_panic_middleware))
        .with_state(state); // 注入状态

    // 启动服务器
    let bind_addr = format!("{}:{}", config.server.host, config.server.port);
    let listener = tokio::net::TcpListener::bind(&bind_addr).await?;

    tracing::info!("服务已启动!");
    tracing::info!(
        "API 文档地址: http://localhost:{}/swagger",
        config.server.port
    );
    tracing::info!("健康检查: http://localhost:{}/", config.server.port);
    tracing::info!("\n测试示例:");
    tracing::info!(
        "  成功: curl http://localhost:{}/users/123",
        config.server.port
    );
    tracing::info!(
        "  失败: curl http://localhost:{}/users/999",
        config.server.port
    );
    tracing::info!(
        "  测试错误: curl http://localhost:{}/test/error",
        config.server.port
    );
    tracing::info!(
        "  测试panic: curl http://localhost:{}/test/panic",
        config.server.port
    );

    axum::serve(listener, app).await.unwrap();

    Ok(())
}
